25032
22378
In Programmiersprachenbüchern wird erklärt, dass Werttypen auf dem Stapel und Referenztypen auf dem Heap erstellt werden, ohne zu erklären, was diese beiden Dinge sind. Ich habe keine klare Erklärung dafür gelesen. Ich verstehe, was ein Stapel ist. Aber,
Wo und was sind sie (physisch im Speicher eines echten Computers)?
Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Was ist ihr Umfang?
Was bestimmt die Größe jedes einzelnen von ihnen?
Was macht einen schneller? 
Der Stapel ist der Speicher, der als Arbeitsbereich für einen Ausführungsthread reserviert ist. Wenn eine Funktion aufgerufen wird, wird ein Block oben im Stapel für lokale Variablen und einige Buchhaltungsdaten reserviert. Wenn diese Funktion zurückkehrt, wird der Block nicht mehr verwendet und kann beim nächsten Aufruf einer Funktion verwendet werden. Der Stapel wird immer in einer LIFO-Reihenfolge (last in first out) reserviert. Der zuletzt reservierte Block ist immer der nächste freizugebende Block. Dies macht es wirklich einfach, den Stapel im Auge zu behalten. Das Befreien eines Blocks vom Stapel ist nichts anderes als das Anpassen eines Zeigers.
Der Heap ist Speicher, der für die dynamische Zuordnung reserviert ist. Im Gegensatz zum Stapel gibt es kein erzwungenes Muster für die Zuweisung und Freigabe von Blöcken aus dem Heap. Sie können einen Block jederzeit zuweisen und jederzeit freigeben. Dies macht es viel komplexer, zu verfolgen, welche Teile des Heaps zu einem bestimmten Zeitpunkt zugewiesen oder frei sind. Es stehen viele benutzerdefinierte Heap-Allokatoren zur Verfügung, um die Heap-Leistung für verschiedene Verwendungsmuster zu optimieren.
Jeder Thread erhält einen Stapel, während es normalerweise nur einen Heap für die Anwendung gibt (obwohl es nicht ungewöhnlich ist, mehrere Heaps für verschiedene Zuordnungstypen zu haben).
So beantworten Sie Ihre Fragen direkt:
Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Das Betriebssystem weist den Stapel jedem Thread auf Systemebene zu, wenn der Thread erstellt wird. In der Regel wird das Betriebssystem von der Sprachlaufzeit aufgerufen, um den Heap für die Anwendung zuzuweisen.
Was ist ihr Umfang?
Der Stapel ist an einen Thread angehängt. Wenn der Thread beendet wird, wird der Stapel zurückgefordert. Der Heap wird normalerweise beim Start der Anwendung zur Laufzeit zugewiesen und beim Beenden der Anwendung (technischer Prozess) zurückgefordert.
Was bestimmt die Größe jedes einzelnen von ihnen?
Die Größe des Stapels wird festgelegt, wenn ein Thread erstellt wird. Die Größe des Heapspeichers wird beim Start der Anwendung festgelegt, kann jedoch bei Bedarf an Speicherplatz zunehmen (der Allokator fordert mehr Speicher vom Betriebssystem an).
Was macht einen schneller?
Der Stapel ist schneller, da das Zugriffsmuster das Zuweisen und Freigeben von Speicher von ihm trivial macht (ein Zeiger / eine Ganzzahl wird einfach inkrementiert oder dekrementiert), während der Heap eine viel komplexere Buchhaltung bei einer Zuweisung oder Freigabe aufweist. Außerdem wird jedes Byte im Stapel sehr häufig wiederverwendet, was bedeutet, dass es dem Cache des Prozessors zugeordnet wird, was es sehr schnell macht. Ein weiterer Leistungseinbruch für den Heap besteht darin, dass der Heap, der hauptsächlich eine globale Ressource ist, normalerweise multithreading-sicher sein muss, d. H. Jede Zuweisung und Freigabe muss - normalerweise - mit "allen" anderen Heap-Zugriffen im Programm synchronisiert werden.
Eine klare Demonstration:
Bildquelle: vikashazrati.wordpress.com
|
Stapel:
Wie der Heap im Computer-RAM gespeichert.
Auf dem Stapel erstellte Variablen verlassen den Gültigkeitsbereich und werden automatisch freigegeben.
Im Vergleich zu Variablen auf dem Heap viel schneller zuzuweisen.
Implementiert mit einer tatsächlichen Stack-Datenstruktur.
Speichert lokale Daten, Rücksprungadressen, die für die Parameterübergabe verwendet werden.
Kann einen Stapelüberlauf haben, wenn zu viel Stapel verwendet wird (meistens aufgrund einer unendlichen oder zu tiefen Rekursion, sehr großer Zuordnungen).
Auf dem Stapel erstellte Daten können ohne Zeiger verwendet werden.
Sie würden den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen und er nicht zu groß ist.
Normalerweise hat eine maximale Größe bereits festgelegt, wenn Ihr Programm startet.
Haufen:
Wird wie der Stack im Computer-RAM gespeichert.
In C ++ müssen Variablen auf dem Heap manuell zerstört werden und dürfen niemals außerhalb des Gültigkeitsbereichs liegen. Die Daten werden mit delete, delete [] oder free freigegeben.
Langsameres Zuweisen im Vergleich zu Variablen auf dem Stapel.
Wird bei Bedarf verwendet, um einen Datenblock zur Verwendung durch das Programm zuzuweisen.
Kann fragmentiert sein, wenn es viele Zuweisungen und Freigaben gibt.
In C ++ oder C werden auf dem Heap erstellte Daten durch Zeiger angezeigt und mit new bzw. malloc zugewiesen.
Kann zu Zuordnungsfehlern führen, wenn die Zuweisung eines zu großen Puffers angefordert wird.
Sie würden den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie viele Daten zuweisen müssen.
Verantwortlich für Speicherlecks.
Beispiel:
int foo ()
{
char * pBuffer; // <- noch nichts zugeordnet (außer dem Zeiger selbst, der hier auf dem Stapel zugeordnet ist).
bool b = true; // Auf dem Stapel zugeordnet.
wenn (b)
{
// 500 Bytes auf dem Stapel erstellen
Zeichenpuffer [500];
// 500 Bytes auf dem Heap erstellen
pBuffer = neues Zeichen [500];
} // <- Puffer wird hier freigegeben, pBuffer nicht
} // <--- Ups, es gibt einen Speicherverlust, ich hätte delete [] pBuffer;
|
Der wichtigste Punkt ist, dass Heap und Stack allgemeine Begriffe für die Zuweisung von Speicher sind. Sie können auf viele verschiedene Arten implementiert werden, und die Begriffe gelten für die Grundkonzepte.
In einem Stapel von Gegenständen liegen die Gegenstände in der Reihenfolge, in der sie dort platziert wurden, übereinander, und Sie können nur den obersten entfernen(ohne das Ganze umzustürzen).
Die Einfachheit eines Stapels besteht darin, dass Sie keine Tabelle verwalten müssen, die einen Datensatz für jeden Abschnitt des zugewiesenen Speichers enthält. Die einzige Statusinformation, die Sie benötigen, ist ein einzelner Zeiger auf das Ende des Stapels. Zum Zuweisen und Aufheben der Zuweisung erhöhen und verringern Sie einfach diesen einzelnen Zeiger. Hinweis: Manchmal kann ein Stapel implementiert werden, der am oberen Rand eines Speicherabschnitts beginnt und sich nach unten erstreckt, anstatt nach oben zu wachsen.
In einem Haufen gibt es keine bestimmte Reihenfolge für die Platzierung von Elementen. Sie können Artikel in beliebiger Reihenfolge erreichen und entfernen, da es keinen eindeutigen "Top" -Element gibt.
Die Heap-Zuweisung erfordert die vollständige Aufzeichnung, welcher Speicher zugewiesen ist und was nicht, sowie einige Overhead-Wartungsarbeiten zur Reduzierung der Fragmentierung, um zusammenhängende Speichersegmente zu finden, die groß genug sind, um der angeforderten Größe zu entsprechen, und so weiter. Der Speicher kann jederzeit freigegeben werden, sodass freier Speicherplatz verbleibt. Manchmal führt ein Speicherzuweiser Wartungsaufgaben aus, z. B. das Defragmentieren des Speichers durch Verschieben des zugewiesenen Speichers oder das Sammeln von Speicherplatz. Er identifiziert zur Laufzeit, wenn der Speicher nicht mehr im Umfang ist, und gibt die Zuordnung auf.
Diese Bilder sollten die beiden Möglichkeiten zum Zuweisen und Freigeben von Speicher in einem Stapel und einem Heap recht gut beschreiben. Yum!
Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Wie bereits erwähnt, sind Heap und Stack allgemeine Begriffe und können auf viele Arten implementiert werden. Computerprogramme haben normalerweise einen Stapel, der als Aufrufstapel bezeichnet wird und Informationen speichert, die für die aktuelle Funktion relevant sind, wie z. B. einen Zeiger auf die Funktion, von der aus sie aufgerufen wurde, und lokale Variablen. Da Funktionen andere Funktionen aufrufen und dann zurückkehren, wächst und schrumpft der Stapel, um Informationen von den Funktionen weiter unten im Aufrufstapel zu speichern. Ein Programm hat nicht wirklich die Laufzeitsteuerung. Es wird von der Programmiersprache, dem Betriebssystem und sogar der Systemarchitektur bestimmt.
Ein Heap ist ein allgemeiner Begriff für jeden Speicher, der dynamisch und zufällig zugewiesen wird. d.h. nicht in Ordnung. Der Speicher wird normalerweise vom Betriebssystem zugewiesen, wobei die Anwendung API-Funktionen aufruft, um diese Zuordnung vorzunehmen. Für die Verwaltung des dynamisch zugewiesenen Speichers ist ein gewisser Aufwand erforderlich, der normalerweise vom Laufzeitcode der verwendeten Programmiersprache oder -umgebung verwaltet wird.
Was ist ihr Umfang?
Der Aufrufstapel ist ein so niedriges Konzept, dass er sich nicht auf den Umfang im Sinne der Programmierung bezieht. Wenn Sie Code zerlegen, werden relative Verweise auf Zeigerstile auf Teile des Stapels angezeigt. In Bezug auf eine übergeordnete Sprache legt die Sprache jedoch ihre eigenen Bereichsregeln fest. Ein wichtiger Aspekt eines Stapels ist jedoch, dass nach der Rückkehr einer Funktion alles, was für diese Funktion lokal ist, sofort vom Stapel freigegeben wird. Das funktioniert so, wie Sie es erwarten würden, wenn man bedenkt, wie Ihre Programmiersprachen funktionieren. In einem Haufen ist es auch schwierig zu definieren. Der Bereich ist alles, was vom Betriebssystem verfügbar gemacht wird, aber Ihre Programmiersprache fügt wahrscheinlich die Regeln hinzu, die festlegen, was ein "Bereich" in Ihrer Anwendung ist. Die Prozessorarchitektur und das Betriebssystem verwenden die virtuelle Adressierung, die der Prozessor in physische Adressen übersetzt, und es gibt Seitenfehler usw. Sie verfolgen, welche Seiten zu welchen Anwendungen gehören. Sie müssen sich darüber jedoch nie wirklich Gedanken machen, da Sie nur die Methode verwenden, die Ihre Programmiersprache verwendet, um Speicher zuzuweisen und freizugeben und nach Fehlern zu suchen (wenn die Zuweisung / Freigabe aus irgendeinem Grund fehlschlägt).
Was bestimmt die Größe jedes einzelnen von ihnen?
Auch dies hängt von der Sprache, dem Compiler, dem Betriebssystem und der Architektur ab. Ein Stapel ist normalerweise vorab zugewiesen, da er per Definition ein zusammenhängender Speicher sein muss. Der Sprachcompiler oder das Betriebssystem bestimmen seine Größe. Sie speichern keine großen Datenmengen auf dem Stapel, daher ist er groß genug, um niemals vollständig verwendet zu werden, außer in Fällen unerwünschter endloser Rekursion (daher "Stapelüberlauf") oder anderer ungewöhnlicher Programmierentscheidungen.
Ein Heap ist ein allgemeiner Begriff für alles, was dynamisch zugewiesen werden kann. Je nachdem, wie Sie es betrachten, ändert sich die Größe ständig. In modernen Prozessoren und Betriebssystemen ist die genaue Funktionsweise ohnehin sehr abstrahiert, sodass Sie sich normalerweise keine Gedanken darüber machen müssen, wie es tief im Inneren funktioniert, außer dass Sie (in Sprachen, in denen Sie dies zulassen) keinen Speicher verwenden dürfen Sie haben noch keinen Speicher zugewiesen, den Sie freigegeben haben.
Was macht einen schneller?
Der Stapel ist schneller, da der gesamte freie Speicher immer zusammenhängend ist. Es muss keine Liste aller Segmente des freien Speichers geführt werden, nur ein einziger Zeiger auf den aktuellen oberen Rand des Stapels. Compiler speichern diesen Zeiger normalerweise zu diesem Zweck in einem speziellen, schnellen Register. Darüber hinaus konzentrieren sich nachfolgende Operationen auf einem Stapel normalerweise auf sehr nahe gelegene Speicherbereiche, was auf einer sehr niedrigen Ebene für die Optimierung durch den Prozessor auf dem Chip gut istCaches.
|
(Ich habe diese Antwort von einer anderen Frage verschoben, die mehr oder weniger ein Betrüger dieser Frage war.)
Die Antwort auf Ihre Frage ist implementierungsspezifisch und kann je nach Compiler und Prozessorarchitektur variieren. Hier ist jedoch eine vereinfachte Erklärung.
Sowohl der Stapel als auch der Heap sind Speicherbereiche, die vom zugrunde liegenden Betriebssystem zugewiesen werden (häufig virtueller Speicher, der bei Bedarf dem physischen Speicher zugeordnet wird).
In einer Umgebung mit mehreren Threads verfügt jeder Thread über einen eigenen, völlig unabhängigen Stapel, der sich jedoch den Heap teilt. Der gleichzeitige Zugriff muss auf dem Heap gesteuert werden und ist auf dem Stapel nicht möglich.
Der Haufen
Der Heap enthält eine verknüpfte Liste der verwendeten und freien Blöcke. Neue Zuordnungen auf dem Heap (von new oder malloc) werden erfüllt, indem aus einem der freien Blöcke ein geeigneter Block erstellt wird. Dies erfordert die Aktualisierung der Liste der Blöcke auf dem Heap. Diese Metainformationen zu den Blöcken auf dem Heap werden auch häufig in einem kleinen Bereich direkt vor jedem Block auf dem Heap gespeichert.
Wenn der Heap wächst, werden häufig neue Blöcke von niedrigeren Adressen zu höheren Adressen zugewiesen. Sie können sich den Heap also als einen Heap von Speicherblöcken vorstellen, dessen Größe mit der Zuweisung von Speicher zunimmt. Wenn der Heap für eine Zuordnung zu klein ist, kann die Größe häufig erhöht werden, indem mehr Speicher vom zugrunde liegenden Betriebssystem abgerufen wird.
Das Zuweisen und Freigeben vieler kleiner Blöcke kann den Heap in einem Zustand belassen, in dem viele kleine freie Blöcke zwischen den verwendeten Blöcken verteilt sind. Eine Anforderung zum Zuweisen eines großen Blocks kann fehlschlagen, da keiner der freien Blöcke groß genug ist, um die Zuweisungsanforderung zu erfüllen, obwohl die kombinierte Größe der freien Blöcke möglicherweise groß genug ist. Dies wird als Heap-Fragmentierung bezeichnet.
Wenn ein verwendeter Block, der an einen freien Block angrenzt, freigegeben wird, kann der neue freie Block mit dem benachbarten freien Block zusammengeführt werden, um einen größeren freien Block zu erzeugen, der die Fragmentierung des Heaps effektiv verringert.
Der Stapel
Der Stapel arbeitet häufig eng zusammen mit einem speziellen Register auf der CPU, das als Stapelzeiger bezeichnet wird. Zu Beginn zeigt der Stapelzeiger auf die Oberseite des Stapels (die höchste Adresse auf dem Stapel).
Die CPU verfügt über spezielle Anweisungen, um Werte auf den Stapel zu verschieben und vom Stapel zurückzuschieben. Jeder Push speichert den Wert an der aktuellen Position des Stapelzeigers und verringert den Stapelzeiger. Ein Popup ruft den Wert ab, auf den der Stapelzeiger zeigt, und erhöht dann den Stapelzeiger (nicht zu verwechseln durch die Tatsache, dass das Hinzufügen eines Werts zum Stapel den Stapelzeiger verringert und das Entfernen eines Werts ihn erhöht. Denken Sie daran, dass der Stapel wächst der Boden). Die gespeicherten und abgerufenen Werte sind die Werte der CPU-Register.
Wenn eine Funktion aufgerufen wird, verwendet die CPU spezielle Befehle, die den aktuellen Befehlszeiger drücken, d. H. Die Adresse des auf dem Stapel ausgeführten Codes. Die CPU springt dann durch Einstellen von auf die Funktion
Befehlszeiger auf die Adresse der aufgerufenen Funktion. Später, wenn die Funktion zurückkehrt, wird der alte Anweisungszeiger vom Stapel entfernt und die Ausführung wird unmittelbar nach dem Aufruf der Funktion am Code fortgesetzt.
Wenn eine Funktion eingegeben wird, wird der Stapelzeiger verringert, um mehr Platz auf dem Stapel für lokale (automatische) Variablen zuzuweisen. Wenn die Funktion eine lokale 32-Bit-Variable hat, werden vier Bytes auf dem Stapel beiseite gelegt. Wenn die Funktion zurückkehrt, wird der Stapelzeiger zurückbewegt, um den zugewiesenen Bereich freizugeben.
Wenn eine Funktion Parameter hat, werden diese vor dem Aufruf der Funktion auf den Stapel verschoben. Der Code in der Funktion kann dann vom aktuellen Stapelzeiger nach oben navigieren, um diese Werte zu lokalisieren.
Verschachtelungsfunktionsaufrufe wirken wie ein Zauber. Jeder neue Aufruf weist Funktionsparameter, die Rücksprungadresse und den Platz für lokale Variablen zu. Diese Aktivierungsdatensätze können für verschachtelte Aufrufe gestapelt werden und werden bei der Rückkehr der Funktionen auf die richtige Weise abgewickelt.
Da der Stapel ein begrenzter Speicherblock ist, können Sie einen Stapelüberlauf verursachen, indem Sie zu viele verschachtelte Funktionen aufrufen und / oder zu viel Speicherplatz für lokale Variablen zuweisen. Oft ist der für den Stapel verwendete Speicherbereich so eingerichtet, dass das Schreiben unter den unteren Rand (die niedrigste Adresse) des Stapels einen Trap oder eine Ausnahme in der CPU auslöst. Diese Ausnahmebedingung kann dann von der Laufzeit erfasst und in eine Art Stapelüberlaufausnahme umgewandelt werden.
Kann eine Funktion auf dem Heap anstelle eines Stapels zugewiesen werden?
Nein, Aktivierungsdatensätze für Funktionen (d. H. Lokale oder automatische Variablen) werden auf dem Stapel zugewiesen, der nicht nur zum Speichern dieser Variablen, sondern auch zum Verfolgen verschachtelter Funktionsaufrufe verwendet wird.
Wie der Heap verwaltet wird, hängt wirklich von der Laufzeitumgebung ab. C verwendet malloc und C ++ verwendet new, aber viele andere Sprachen haben Garbage Collection.
Der Stapel ist jedoch ein Merkmal auf niedrigerer Ebene, das eng mit der Prozessorarchitektur verbunden ist. Das Wachsen des Haufens, wenn nicht genügend Platz vorhanden ist, ist seitdem nicht allzu schwierigEs kann in dem Bibliotheksaufruf implementiert werden, der den Heap verarbeitet. Das Wachsen des Stapels ist jedoch häufig nicht möglich, da der Stapelüberlauf nur dann erkannt wird, wenn es zu spät ist. Das Herunterfahren des Ausführungsthreads ist die einzig praktikable Option.
|
Im folgenden C # -Code
public void Method1 ()
{
int i = 4;
int y = 2;
class1 cls1 = new class1 ();
}}
So wird der Speicher verwaltet
Lokale Variablen, die nur so lange dauern müssen, wie der Funktionsaufruf im Stapel abläuft. Der Heap wird für Variablen verwendet, deren Lebensdauer wir nicht wirklich kennen, aber wir erwarten, dass sie eine Weile dauern. In den meisten Sprachen ist es wichtig, dass wir zur Kompilierungszeit wissen, wie groß eine Variable ist, wenn wir sie auf dem Stapel speichern möchten.
Objekte (deren Größe beim Aktualisieren variiert) werden auf dem Heap gespeichert, da wir zum Zeitpunkt der Erstellung nicht wissen, wie lange sie dauern werden. In vielen Sprachen wird der Heap durch Müll gesammelt, um Objekte (wie das Objekt cls1) zu finden, die keine Referenzen mehr haben.
In Java werden die meisten Objekte direkt in den Heap verschoben. In Sprachen wie C / C ++ können Strukturen und Klassen häufig auf dem Stapel verbleiben, wenn Sie nicht mit Zeigern arbeiten.
Weitere Informationen finden Sie hier:
Der Unterschied zwischen Stapel- und Heapspeicherzuordnung «timmurphy.org
und hier:
Erstellen von Objekten auf dem Stapel und dem Heap
Dieser Artikel ist die Quelle des obigen Bildes: Sechs wichtige .NET-Konzepte: Stapel, Heap, Werttypen, Referenztypen, Boxen und Unboxing - CodeProject
Beachten Sie jedoch, dass es einige Ungenauigkeiten enthalten kann.
|
Der Stapel
Wenn Sie eine Funktion aufrufen, werden die Argumente für diese Funktion sowie ein anderer Overhead auf den Stapel gelegt. Dort werden auch einige Informationen gespeichert (z. B. wohin Sie bei der Rückgabe gehen müssen).
Wenn Sie eine Variable in Ihrer Funktion deklarieren, wird diese Variable auch dem Stapel zugewiesen.
Die Freigabe des Stapels ist ziemlich einfach, da Sie die Freigabe immer in der umgekehrten Reihenfolge vornehmen, in der Sie die Zuweisung vornehmen. Beim Eingeben von Funktionen werden Stapelmaterial hinzugefügt, die entsprechenden Daten werden beim Beenden entfernt. Dies bedeutet, dass Sie dazu neigen, in einem kleinen Bereich des Stapels zu bleiben, es sei denn, Sie rufen viele Funktionen auf, die viele andere Funktionen aufrufen (oder eine rekursive Lösung erstellen).
Der Haufen
Der Heap ist ein generischer Name für den Ort, an dem Sie die von Ihnen erstellten Daten im laufenden Betrieb ablegen. Wenn Sie nicht wissen, wie viele Raumschiffe Ihr Programm erstellen wird, verwenden Sie wahrscheinlich den neuen (oder malloc oder gleichwertigen) Operator, um jedes Raumschiff zu erstellen. Diese Zuordnung wird noch eine Weile bestehen bleiben, daher werden wir die Dinge wahrscheinlich in einer anderen Reihenfolge freigeben, als wir sie erstellt haben.
Daher ist der Heap weitaus komplexer, da es am Ende Speicherbereiche gibt, die nicht verwendet werden, verschachtelt mit Blöcken, die - Speicher wird fragmentiert. Es ist ein schwieriges Problem, freien Speicher in der Größe zu finden, die Sie benötigen. Aus diesem Grund sollte der Heap vermieden werden (obwohl er immer noch häufig verwendet wird).
Implementierung
Die Implementierung sowohl des Stacks als auch des Heaps hängt normalerweise von der Laufzeit / dem Betriebssystem ab. Oft erstellen Spiele und andere Anwendungen, die leistungskritisch sind, ihre eigenen Speicherlösungen, die einen großen Teil des Speichers aus dem Heap holen und ihn dann intern verteilen, um zu vermeiden, dass das Betriebssystem auf den Speicher angewiesen ist.
Dies ist nur dann praktisch, wenn Ihre Speichernutzung stark von der Norm abweicht - d. H. Bei Spielen, bei denen Sie in einem großen Vorgang ein Level laden und in einem anderen großen Vorgang das ganze Los wegwerfen können.
Physischer Speicherort
Dies ist weniger relevant als Sie denken, da eine Technologie namens Virtual Memory Ihr Programm glauben lässt, dass Sie Zugriff auf eine bestimmte Adresse haben, an der sich die physischen Daten an einem anderen Ort befinden (sogar auf der Festplatte!). Die Adressen, die Sie für den Stapel erhalten, werden in aufsteigender Reihenfolge angezeigt, wenn Ihr Aufrufbaum tiefer wird. Die Adressen für den Heap sind nicht vorhersehbar (d. H. Implementierungsspezifisch) und offen gesagt nicht wichtig.
|
Zur Verdeutlichung hat diese Antwort falsche Informationen (Thomas hat seine Antwort nach Kommentaren korrigiert, cool :)). Andere Antworten vermeiden es einfach zu erklären, was statische Zuordnung bedeutet. Daher werde ich die drei Hauptformen der Zuordnung erläutern und wie sie sich normalerweise auf das Heap-, Stack- und Datensegment unten beziehen. Ich werde auch einige Beispiele in C / C ++ und Python zeigen, um den Leuten das Verständnis zu erleichtern.
"Statische" (statisch zugewiesene) AKA-Variablen werden dem Stapel nicht zugewiesen. Gehen Sie nicht davon aus - viele Leute tun dies nur, weil "statisch" sehr nach "Stapel" klingt. Sie existieren tatsächlich weder im Stapel noch im Haufen. Sie sind Teil des sogenannten Datensegments.
Im Allgemeinen ist es jedoch besser, "Umfang" und "Lebensdauer" als "Stapel" und "Heap" zu berücksichtigen.
Der Bereich bezieht sich darauf, welche Teile des Codes auf eine Variable zugreifen können. Im Allgemeinen denken wir an den lokalen Bereich (auf den nur mit der aktuellen Funktion zugegriffen werden kann) im Vergleich zum globalen Bereich (auf den überall zugegriffen werden kann), obwohl der Bereich viel komplexer werden kann.
Die Lebensdauer bezieht sich darauf, wann eine Variable während der Programmausführung zugewiesen und freigegeben wird. Normalerweise denken wir an statische Zuordnung (variabel)bleibt während der gesamten Dauer des Programms bestehen, sodass es nützlich ist, um dieselben Informationen über mehrere Funktionsaufrufe hinweg zu speichern.) Im Vergleich zur automatischen Zuweisung (Variable bleibt nur während eines einzelnen Aufrufs einer Funktion bestehen, ist es nützlich, Informationen zu speichern, die nur während Ihres Programms verwendet werden Funktion und kann verworfen werden, wenn Sie fertig sind) versus dynamische Zuordnung (Variablen, deren Dauer zur Laufzeit definiert wird, anstelle der Kompilierungszeit wie statisch oder automatisch).
Obwohl die meisten Compiler und Interpreter dieses Verhalten in Bezug auf die Verwendung von Stacks, Heaps usw. ähnlich implementieren, kann ein Compiler diese Konventionen manchmal brechen, wenn er dies wünscht, solange das Verhalten korrekt ist. Beispielsweise kann aufgrund der Optimierung eine lokale Variable nur in einem Register vorhanden sein oder vollständig entfernt werden, obwohl die meisten lokalen Variablen im Stapel vorhanden sind. Wie bereits in einigen Kommentaren erwähnt, können Sie einen Compiler implementieren, der nicht einmal einen Stack oder einen Heap verwendet, sondern einige andere Speichermechanismen (selten durchgeführt, da Stacks und Heaps dafür großartig sind).
Ich werde einen einfachen kommentierten C-Code bereitstellen, um all dies zu veranschaulichen. Der beste Weg zu lernen ist, ein Programm unter einem Debugger auszuführen und das Verhalten zu beobachten. Wenn Sie lieber Python lesen möchten, fahren Sie mit dem Ende der Antwort fort :)
// Statisch im Datensegment zugeordnet, wenn das Programm / die DLL zum ersten Mal geladen wird
// Freigabe beim Beenden des Programms / der DLL
// scope - kann von überall im Code aufgerufen werden
int someGlobalVariable;
// Statisch im Datensegment zugeordnet, wenn das Programm zum ersten Mal geladen wird
// Freigabe beim Beenden des Programms / der DLL
// scope - kann von überall in dieser bestimmten Codedatei aufgerufen werden
static int someStaticVariable;
// "someArgument" wird bei jedem Aufruf von MyFunction auf dem Stapel zugewiesen
// "someArgument" wird freigegeben, wenn MyFunction zurückkehrt
// scope - kann nur innerhalb von MyFunction () aufgerufen werden
void MyFunction (int someArgument) {
// Statisch im Datensegment zugeordnet, wenn das Programm zum ersten Mal geladen wird
// Freigabe beim Beenden des Programms / der DLL
// scope - kann nur innerhalb von MyFunction () aufgerufen werden
static int someLocalStaticVariable;
// Wird bei jedem Aufruf von MyFunction auf dem Stapel zugewiesen
// Freigegeben, wenn MyFunction zurückkehrt
// scope - kann nur innerhalb von MyFunction () aufgerufen werden
int someLocalVariable;
// Bei jedem Aufruf von MyFunction wird dem Stapel ein * Zeiger * zugewiesen
// Dieser Zeiger wird freigegeben, wenn MyFunction zurückkehrt
// scope - Auf den Zeiger kann nur innerhalb von MyFunction () zugegriffen werden.
int * someDynamicVariable;
// Diese Zeile bewirkt, dass Platz für eine Ganzzahl im Heap zugewiesen wird
// wenn diese Zeile ausgeführt wird. Beachten Sie, dass dies nicht am Anfang von ist
// der Aufruf von MyFunction (), wie die automatischen Variablen
// scope - Nur Code in MyFunction () kann auf diesen Bereich zugreifen
// * durch diese bestimmte Variable *.
// Wenn Sie die Adresse jedoch an eine andere Stelle übergeben, diesen Code
// kann auch darauf zugreifen
someDynamicVariable = new int;
// Diese Zeile gibt den Platz für die Ganzzahl im Heap frei.
// Wenn wir es nicht schreiben würden, wäre der Speicher "durchgesickert".
// Beachten Sie einen grundlegenden Unterschied zwischen dem Stapel und dem Heap
// Der Heap muss verwaltet werden. Der Stack wird für uns verwaltet.
someDynamicVariable löschen;
// In anderen Fällen, anstatt diesen Heap-Speicherplatz freizugeben
// speichert die Adresse möglicherweise an einem dauerhafteren Ort, um sie später zu verwenden.
// Einige Sprachen kümmern sich sogar um die Freigabe für Sie ... aber
// Es muss immer zur Laufzeit von einem Mechanismus erledigt werden.
// Wenn die Funktion zurückkehrt, someArgument, someLocalVariable
// und der Zeiger someDynamicVariable werden freigegeben.
// Der Platz, auf den someDynamicVariable zeigt, war bereits vorhanden
// vor der Rückkehr freigegeben.
Rückkehr;
}}
// Beachten Sie, dass someGlobalVariable, someStaticVariable und
// someLocalStaticVariable existiert weiterhin und ist es nicht
// freigegeben, bis das Programm beendet wird.
Ein besonders ergreifendes Beispiel dafür, warum es wichtig ist, zwischen Lebensdauer und Gültigkeitsbereich zu unterscheiden, ist, dass eine Variable einen lokalen Gültigkeitsbereich, aber eine statische Lebensdauer haben kann - zum Beispiel "someLocalStaticVariable" im obigen Codebeispiel. Solche Variablen können unsere allgemeinen, aber informellen Namensgewohnheiten sehr verwirrend machen. Wenn wir beispielsweise "lokal" sagen, meinen wir normalerweise "automatisch zugewiesene Variable mit lokalem Gültigkeitsbereich" und wenn wir global sagen, meinen wir normalerweise "statisch zugewiesene Variable mit globalem Gültigkeitsbereich". Leider sagen viele Leute, wenn es um Dinge wie "statisch zugewiesene Variablen mit Dateibereich" geht, nur ... "huh ???".
Einige der Syntaxoptionen in C / C ++ verschärfen dieses Problem - zum Beispiel denken viele Leute, dass globale Variablen aufgrund der unten gezeigten Syntax nicht "statisch" sind.
int var1; // Hat globalen Bereich und statische Zuordnung
statisch int var2; // Hat Dateibereich und statische Zuordnung
int main () {return 0;}
Beachten Sie, dass das Einfügen des Schlüsselworts "static" in die obige Deklaration verhindert, dass var2 einen globalen Gültigkeitsbereich hat. Trotzdem hat die globale var1 eine statische Zuordnung. Das ist nichtintuitiv! Aus diesem Grund versuche ich, bei der Beschreibung des Bereichs niemals das Wort "statisch" zu verwenden und stattdessen etwas wie "Datei" oder "Dateibegrenzter" Bereich zu sagen. Viele Benutzer verwenden jedoch den Ausdruck "statisch" oder "statischer Bereich", um eine Variable zu beschreiben, auf die nur über eine Codedatei zugegriffen werden kann. Im Kontext der Lebensdauer bedeutet "statisch" immer, dass die Variable beim Programmstart zugewiesen und beim Beenden des Programms freigegeben wird.
Einige Leute halten diese Konzepte für C / C ++ -spezifisch. Sie sind nicht. Das folgende Python-Beispiel zeigt beispielsweise alle drei Arten der Zuordnung (bei interpretierten Sprachen sind einige subtile Unterschiede möglich, auf die ich hier nicht näher eingehen werde).
von datetime import datetime
Klasse Tier:
_FavoriteFood = 'Undefiniert' # _FavoriteFood ist statisch zugeordnet
def PetAnimal (Selbst):
curTime = datetime.time (datetime.now ()) # curTime wird automatisch zugewiesen
print ("Danke, dass du mich gestreichelt hast. Aber es ist" + str (curTime) + ", du solltest mich füttern. Mein Lieblingsessen ist" + self._FavoriteFood ")
Klasse Katze (Tier):
_FavoriteFood = 'Thunfisch' # Hinweis: Da wir überschreiben, hat die Cat-Klasse eine eigene statisch zugewiesene _FavoriteFood-Variable, die sich von der von Animal unterscheidet
Klasse Hund (Tier):
_FavoriteFood = 'steak' # Ebenso erhält die Dog-Klasse eine eigene statische Variable. Wichtig zu beachten - diese eine statische Variable wird von allen Instanzen von Dog gemeinsam genutzt, daher ist sie nicht dynamisch!
if __name__ == "__main__":
Whisker = Cat () # Dynamisch zugeordnet
fido = Dog () # Dynamisch zugeordnet
rinTinTin = Dog () # Dynamisch zugeordnet
Whisker.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
Dog._FavoriteFood = 'Milchknochen'
Whisker.PetAnimal ()
fido.PetAnimal ()
rinTinTin.PetAnimal ()
# Ausgabe ist:
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.255000, du solltest mich füttern. Mein Lieblingsessen ist Thunfisch
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.255000, du solltest mich füttern. Mein Lieblingsessen ist Steak
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.255000, du solltest mich füttern. Mein Lieblingsessen ist Steak
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.255000, du solltest mich füttern. Mein Lieblingsessen ist Thunfisch
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.255000, du solltest mich füttern. Mein Lieblingsessen sind Milchknochen
# Danke, dass du mich gestreichelt hast. Aber es ist 13: 05: 02.256000, du solltest mich füttern. Mein Lieblingsessen sind Milchknochen
|
Andere haben die breiten Striche ziemlich gut beantwortet, deshalb werde ich ein paar Details einbringen.
Stapel und Haufen müssen nicht singulär sein. Eine häufige Situation, in der Sie mehr als einen Stapel haben, ist, wenn Sie mehr als einen Thread in einem Prozess haben. In diesem Fall hat jeder Thread seinen eigenen Stapel. Sie können auch mehr als einen Heap haben. Einige DLL-Konfigurationen können beispielsweise dazu führen, dass unterschiedliche DLLs von unterschiedlichen Heaps zugewiesen werden. Daher ist es im Allgemeinen eine schlechte Idee, den von einer anderen Bibliothek zugewiesenen Speicher freizugeben.
In C können Sie den Vorteil einer Zuweisung mit variabler Länge durch die Verwendung von Zuweisung erhalten, die auf dem Stapel zugewiesen wird, im Gegensatz zu Zuweisung, die auf dem Heap zugewiesen wird. Dieser Speicher überlebt Ihre return-Anweisung nicht, ist jedoch für einen Arbeitspuffer nützlich.
Es ist nicht kostenlos, unter Windows einen riesigen temporären Puffer zu erstellen, von dem Sie nicht viel verwenden. Dies liegt daran, dass der Compiler eine Stapelprüfschleife generiert, die bei jeder Eingabe Ihrer Funktion aufgerufen wird, um sicherzustellen, dass der Stapel vorhanden ist (da Windows eine einzelne Schutzseite am Ende Ihres Stapels verwendet, um zu erkennen, wann der Stapel vergrößert werden muss. Wenn Sie mehr als eine Seite am Ende des Stapels auf den Speicher zugreifen, stürzen Sie ab. Beispiel:
void myfunction ()
{
char big [10000000];
// Mach etwas, das nur für die ersten 1K von großen 99% der Zeit verwendet wird.
}}
|
Andere haben Ihre Frage direkt beantwortet, aber wenn Sie versuchen, den Stapel und den Heap zu verstehen, ist es hilfreich, das Speicherlayout eines herkömmlichen UNIX-Prozesses (ohne Threads und mmap () -basierte Allokatoren) zu berücksichtigen. Die Webseite zum Speicherverwaltungsglossar enthält ein Diagramm dieses Speicherlayouts.
Der Stapel und der Heap befinden sich traditionell an entgegengesetzten Enden des virtuellen Adressraums des Prozesses. Der Stack wächst beim Zugriff automatisch bis zu einer vom Kernel festgelegten Größe (die mit setrlimit (RLIMIT_STACK, ...) angepasst werden kann). Der Heap wächst, wenn der Speicherzuweiser den Systemaufruf brk () oder sbrk () aufruft und mehr Seiten des physischen Speichers dem virtuellen Adressraum des Prozesses zuordnet.
In Systemen ohne virtuellen Speicher, wie z. B. einigen eingebetteten Systemen, gilt häufig das gleiche Grundlayout, außer dass der Stapel und der Heap eine feste Größe haben. In anderen eingebetteten Systemen (z. B. solchen, die auf Microchip PIC-Mikrocontrollern basieren) ist der Programmstapel jedoch ein separater Speicherblock, der nicht durch Datenbewegungsanweisungen adressiert werden kann und nur durch Programmflussanweisungen (Aufruf, Rückkehr usw.). Andere Architekturen, wie z. B. Intel Itanium-Prozessoren, haben mehrere Stapel. In diesem Sinne ist der Stack ein Element der CPU-Architektur.
|
Der Stapel ist ein TeilSpeicher, der über mehrere wichtige Anweisungen in Assemblersprache bearbeitet werden kann, z. B. 'pop' (Entfernen und Zurückgeben eines Werts vom Stapel) und 'push' (Verschieben eines Werts in den Stapel), aber auch Aufrufen (Aufrufen einer Unterroutine - dies drückt die Adresse, um zum Stapel zurückzukehren) und zurück (Rückkehr von einem Unterprogramm - dies entfernt die Adresse vom Stapel und springt dorthin). Dies ist der Speicherbereich unterhalb des Stapelzeigerregisters, der nach Bedarf eingestellt werden kann. Der Stapel wird auch zum Übergeben von Argumenten an Unterroutinen und zum Beibehalten der Werte in Registern vor dem Aufrufen von Unterroutinen verwendet.
Der Heap ist ein Teil des Speichers, der vom Betriebssystem an eine Anwendung übergeben wird, normalerweise über einen Systemaufruf wie malloc. Unter modernen Betriebssystemen besteht dieser Speicher aus einer Reihe von Seiten, auf die nur der aufrufende Prozess Zugriff hat.
Die Größe des Stapels wird zur Laufzeit festgelegt und wächst im Allgemeinen nach dem Start des Programms nicht mehr. In einem C-Programm muss der Stapel groß genug sein, um jede in jeder Funktion deklarierte Variable aufzunehmen. Der Heap wächst nach Bedarf dynamisch, aber das Betriebssystem führt letztendlich den Aufruf durch (häufig wächst der Heap um mehr als den von malloc angeforderten Wert, sodass zumindest einige zukünftige Mallocs nicht zum Kernel zurückkehren müssen Holen Sie sich mehr Speicher. Dieses Verhalten ist oft anpassbar.
Da Sie den Stapel vor dem Starten des Programms zugewiesen haben, müssen Sie nie mallocieren, bevor Sie den Stapel verwenden können. Dies ist also ein kleiner Vorteil. In der Praxis ist es sehr schwer vorherzusagen, was in modernen Betriebssystemen mit virtuellen Speichersubsystemen schnell und was langsam sein wird, da die Implementierung und Speicherung der Seiten ein Implementierungsdetail ist.
|
Was ist ein Stapel?
Ein Stapel ist ein Stapel von Objekten, normalerweise einer, der ordentlich angeordnet ist.
Stapel in Computerarchitekturen sind Speicherbereiche, in denen Daten auf Last-In-First-Out-Weise hinzugefügt oder entfernt werden.
In einer Multithread-Anwendung hat jeder Thread seinen eigenen Stapel.
Was ist ein Haufen?
Ein Haufen ist eine unordentliche Sammlung von Dingen, die sich willkürlich stapeln.
In Computerarchitekturen ist der Heap ein Bereich mit dynamisch zugewiesenem Speicher, der automatisch vom Betriebssystem oder der Speichermanagerbibliothek verwaltet wird.
Der Speicher auf dem Heap wird während der Programmausführung regelmäßig zugewiesen, freigegeben und in der Größe geändert. Dies kann zu einem Problem führen, das als Fragmentierung bezeichnet wird.
Fragmentierung tritt auf, wenn Speicherobjekte mit kleinen Zwischenräumen zugewiesen werden, die zu klein sind, um zusätzliche Speicherobjekte aufzunehmen.
Das Nettoergebnis ist ein Prozentsatz des Heapspeichers, der nicht für weitere Speicherzuweisungen verwendet werden kann.
Beide zusammen
In einer Multithread-Anwendung hat jeder Thread seinen eigenen Stapel. Alle verschiedenen Threads teilen sich jedoch den Heap.
Da sich die verschiedenen Threads den Heap in einer Multithread-Anwendung gemeinsam nutzen, bedeutet dies auch, dass eine gewisse Koordination zwischen den Threads erforderlich ist, damit sie nicht versuchen, auf dieselben Speicherelemente im Heap bei zuzugreifen und diese zu bearbeiten die selbe Zeit.
Was ist schneller - der Stapel oder der Haufen? Und warum?
Der Stapel ist viel schneller als der Haufen.
Dies liegt an der Art und Weise, wie Speicher auf dem Stapel zugewiesen wird.
Das Zuweisen von Speicher auf dem Stapel ist so einfach wie das Bewegen des Stapelzeigers nach oben.
Für Programmieranfänger ist es wahrscheinlich eine gute Idee, den Stack zu verwenden, da dies einfacher ist.
Da der Stapel klein ist, sollten Sie ihn verwenden, wenn Sie genau wissen, wie viel Speicher Sie für Ihre Daten benötigen, oder wenn Sie wissen, dass Ihre Daten sehr klein sind.
Es ist besser, den Heap zu verwenden, wenn Sie wissen, dass Sie viel Speicher für Ihre Daten benötigen oder einfach nicht sicher sind, wie viel Speicher Sie benötigen (wie bei einem dynamischen Array).
Java-Speichermodell
Der Stapel ist der Speicherbereich, in dem lokale Variablen (einschließlich Methodenparameter) gespeichert sind. Bei Objektvariablen handelt es sich lediglich um Verweise (Zeiger) auf die tatsächlichen Objekte auf dem Heap.
Jedes Mal, wenn ein Objekt instanziiert wird, wird ein Teil des Heapspeichers beiseite gelegt, um die Daten (den Status) dieses Objekts zu speichern. Da Objekte andere Objekte enthalten können, können einige dieser Daten tatsächlich Verweise auf diese verschachtelten Objekte enthalten.
|
Ich denke, viele andere Leute haben Ihnen in dieser Angelegenheit größtenteils richtige Antworten gegeben.
Ein Detail, das jedoch übersehen wurde, ist, dass der "Haufen" tatsächlich wahrscheinlich als "freier Laden" bezeichnet werden sollte. Der Grund für diese Unterscheidung ist, dass der ursprüngliche freie Speicher mit einer Datenstruktur implementiert wurde, die als "Binomialheap" bekannt ist. Aus diesem Grund war die Zuweisung aus frühen Implementierungen von malloc () / free () die Zuweisung von einem Haufen. In der heutigen Zeit werden die meisten freien Speicher jedoch mit sehr ausgefeilten Datenstrukturen implementiert, die keine Binomialhaufen sind.
|
Mit dem Stack können Sie einige interessante Dinge tun. Zum Beispiel haben Sie Funktionen wie alloca (vorausgesetzt, Sie können die zahlreichen Warnungen bezüglich seiner Verwendung überwinden), was eine Form von malloc istVerwendet speziell den Stapel und nicht den Heap für den Speicher.
Trotzdem sind stapelbasierte Speicherfehler einige der schlimmsten, die ich je erlebt habe. Wenn Sie Heapspeicher verwenden und die Grenzen Ihres zugewiesenen Blocks überschreiten, besteht eine gute Chance, dass ein Segmentfehler ausgelöst wird. (Nicht 100%: Ihr Block kann nebenbei an einen anderen Block angrenzen, den Sie zuvor zugewiesen haben.) Da auf dem Stapel erstellte Variablen jedoch immer aneinander angrenzen, kann das Schreiben außerhalb der Grenzen den Wert einer anderen Variablen ändern. Ich habe gelernt, dass mein Programm immer dann, wenn ich das Gefühl habe, die Gesetze der Logik nicht mehr zu befolgen, wahrscheinlich ein Pufferüberlauf ist.
|
Auf dem Stapel werden einfach lokale Variablen erstellt. Außerdem werden jedes Mal, wenn Sie eine Unterroutine aufrufen, der Programmzähler (Zeiger auf die nächste Maschinenanweisung) und alle wichtigen Register und manchmal die Parameter auf den Stapel verschoben. Dann werden alle lokalen Variablen innerhalb des Unterprogramms auf den Stapel verschoben (und von dort verwendet). Wenn die Unterroutine beendet ist, wird das Zeug wieder vom Stapel genommen. Die PC- und Registerdaten werden abgerufen und wieder dort abgelegt, wo sie sich gerade befinden, sodass Ihr Programm seinen fröhlichen Weg fortsetzen kann.
Der Heap ist der Bereich, aus dem dynamische Speicherzuordnungen für den Speicher erstellt werden (explizite "neue" oder "zugewiesene" Aufrufe). Es handelt sich um eine spezielle Datenstruktur, mit der Speicherblöcke unterschiedlicher Größe und deren Zuordnungsstatus verfolgt werden können.
In "klassischen" Systemen wurde RAM so angelegt, dass der Stapelzeiger am unteren Rand des Speichers begann, der Heap-Zeiger am oberen Rand begann und sie aufeinander zuwuchsen. Wenn sie sich überschneiden, haben Sie nicht mehr genügend RAM. Dies funktioniert jedoch nicht mit modernen Multithread-Betriebssystemen. Jeder Thread muss einen eigenen Stapel haben, und diese können dynamisch erstellt werden.
|
Von WikiAnwser.
Stapel
Wenn eine Funktion oder eine Methode eine andere Funktion aufruft, die wiederum eine andere Funktion usw. aufruft, bleibt die Ausführung all dieser Funktionen ausgesetzt, bis die allerletzte Funktion ihren Wert zurückgibt.
Diese Kette angehaltener Funktionsaufrufe ist der Stapel, da Elemente im Stapel (Funktionsaufrufe) voneinander abhängen.
Der Stapel ist wichtig, um bei der Ausnahmebehandlung und Thread-Ausführung berücksichtigt zu werden.
Haufen
Der Heap ist einfach der Speicher, der von Programmen zum Speichern von Variablen verwendet wird.
Elemente des Heaps (Variablen) haben keine Abhängigkeiten voneinander und können jederzeit nach dem Zufallsprinzip aufgerufen werden.
|
Stapel
Sehr schneller Zugang
Sie müssen Variablen nicht explizit freigeben
Der Speicherplatz wird effizient von der CPU verwaltet, der Speicher wird nicht fragmentiert
Nur lokale Variablen
Begrenzung der Stapelgröße (OS-abhängig)
Die Größe von Variablen kann nicht geändert werden
Haufen
Auf Variablen kann global zugegriffen werden
Keine Begrenzung der Speichergröße
(Relativ) langsamerer Zugriff
Ohne garantierte effiziente Speicherplatznutzung kann der Speicher im Laufe der Zeit fragmentiert werden, wenn Speicherblöcke zugewiesen und dann freigegeben werden
Sie müssen den Speicher verwalten (Sie sind für die Zuweisung und Freigabe von Variablen verantwortlich).
Die Größe von Variablen kann mit realloc () geändert werden.
|
Zusamenfassend
Ein Stapel wird für die statische Speicherzuweisung und ein Heap für die dynamische Speicherzuweisung verwendet, die beide im RAM des Computers gespeichert sind.
Im Detail
Der Stapel
Der Stack ist eine "LIFO" -Datenstruktur (last in, first out), die von der CPU sehr genau verwaltet und optimiert wird. Jedes Mal, wenn eine Funktion eine neue Variable deklariert, wird sie auf den Stapel "geschoben". Jedes Mal, wenn eine Funktion beendet wird, werden alle von dieser Funktion auf den Stapel geschobenen Variablen freigegeben (dh sie werden gelöscht). Sobald eine Stapelvariable freigegeben ist, wird dieser Speicherbereich für andere Stapelvariablen verfügbar.
Der Vorteil der Verwendung des Stapels zum Speichern von Variablen besteht darin, dass der Speicher für Sie verwaltet wird. Sie müssen den Speicher nicht manuell zuweisen oder freigeben, wenn Sie ihn nicht mehr benötigen. Da die CPU den Stapelspeicher so effizient organisiert, ist das Lesen und Schreiben in Stapelvariablen sehr schnell.
Mehr finden Sie hier.
Der Haufen
Der Heap ist ein Bereich des Arbeitsspeichers Ihres Computers, der nicht automatisch für Sie verwaltet wird und von der CPU nicht so streng verwaltet wird. Es ist ein frei schwebender Speicherbereich (und größer). Um Speicher auf dem Heap zuzuweisen, müssen Sie malloc () oder calloc () verwenden, die integrierte C-Funktionen sind. Sobald Sie Speicher auf dem Heap zugewiesen haben, sind Sie dafür verantwortlich, free () zu verwenden, um die Zuweisung dieses Speichers aufzuheben, sobald Sie ihn nicht mehr benötigen.
Wenn Sie dies nicht tun, weist Ihr Programm einen sogenannten Speicherverlust auf. Das heißt, der Speicher auf dem Heap wird weiterhin reserviert (und steht anderen Prozessen nicht zur Verfügung). Wie wir im Abschnitt zum Debuggen sehen werden, gibt es ein Tool namens Valgrind, mit dem Sie Speicherlecks erkennen können.
Im Gegensatz zum Stapel unterliegt der Heap keinen Größenbeschränkungen für die variable Größe (abgesehen von den offensichtlichen physischen Einschränkungen Ihres Computers). Der Heap-Speicher ist etwas langsamer zum Lesen und Schreiben, da Zeiger verwendet werden müssen, um auf den Speicher auf dem Heap zuzugreifen. Wir werden in Kürze über Hinweise sprechen.
Im Gegensatz zum StapelAuf Variablen, die auf dem Heap erstellt wurden, kann von jeder Funktion an einer beliebigen Stelle in Ihrem Programm zugegriffen werden. Heap-Variablen sind im Wesentlichen global.
Mehr finden Sie hier.
Auf dem Stapel zugewiesene Variablen werden direkt im Speicher gespeichert, und der Zugriff auf diesen Speicher ist sehr schnell, und seine Zuordnung wird beim Kompilieren des Programms behandelt. Wenn eine Funktion oder eine Methode eine andere Funktion aufruft, die wiederum eine andere Funktion usw. aufruft, bleibt die Ausführung all dieser Funktionen ausgesetzt, bis die allerletzte Funktion ihren Wert zurückgibt. Der Stapel wird immer in einer LIFO-Reihenfolge reserviert. Der zuletzt reservierte Block ist immer der nächste freizugebende Block. Dies macht es wirklich einfach, den Stapel im Auge zu behalten. Das Lösen eines Blocks vom Stapel ist nichts anderes als das Anpassen eines Zeigers.
Auf dem Heap zugewiesene Variablen haben ihren Speicher zur Laufzeit zugewiesen und der Zugriff auf diesen Speicher ist etwas langsamer, aber die Größe des Heapspeichers ist nur durch die Größe des virtuellen Speichers begrenzt. Elemente des Heaps haben keine Abhängigkeiten voneinander und können jederzeit nach dem Zufallsprinzip aufgerufen werden. Sie können einen Block jederzeit zuweisen und jederzeit freigeben. Dies macht es viel komplexer zu verfolgen, welche Teile des Heaps zu einem bestimmten Zeitpunkt zugewiesen oder frei sind.
Sie können den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen, und er nicht zu groß ist. Sie können den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie viele Daten zuweisen müssen.
In einer Situation mit mehreren Threads hat jeder Thread seinen eigenen, völlig unabhängigen Stapel, aber sie teilen sich den Heap. Der Stapel ist threadspezifisch und der Heap ist anwendungsspezifisch. Der Stapel ist wichtig, um bei der Ausnahmebehandlung und Thread-Ausführung berücksichtigt zu werden.
Jeder Thread erhält einen Stapel, während es normalerweise nur einen Heap für die Anwendung gibt (obwohl es nicht ungewöhnlich ist, mehrere Heaps für verschiedene Zuordnungstypen zu haben).
Wenn die Anwendung zur Laufzeit mehr Heap benötigt, kann sie Speicher aus dem freien Speicher zuweisen, und wenn der Stapel Speicher benötigt, kann sie Speicher aus dem freien Speicher zuweisen, der der Anwendung zugewiesen ist.
Noch mehr Details werden hier und hier gegeben.
Kommen Sie nun zu den Antworten Ihrer Frage.
Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Das Betriebssystem weist den Stapel jedem Thread auf Systemebene zu, wenn der Thread erstellt wird. In der Regel wird das Betriebssystem von der Sprachlaufzeit aufgerufen, um den Heap für die Anwendung zuzuweisen.
Mehr finden Sie hier.
Was ist ihr Umfang?
Bereits oben angegeben.
"Sie können den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen, und er nicht zu groß ist. Sie können den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie müssen viele Daten zuweisen. "
Mehr finden Sie hier.
Was bestimmt die Größe jedes einzelnen von ihnen?
Die Größe des Stapels wird vom Betriebssystem festgelegt, wenn ein Thread erstellt wird. Die Größe des Heaps wird beim Start der Anwendung festgelegt, kann jedoch bei Bedarf an Speicherplatz zunehmen (der Allokator fordert mehr Speicher vom Betriebssystem an).
Was macht einen schneller?
Die Stapelzuweisung ist viel schneller, da nur der Stapelzeiger bewegt wird. Wenn Sie Speicherpools verwenden, können Sie eine vergleichbare Leistung bei der Heap-Zuweisung erzielen. Dies ist jedoch mit einer geringfügig zusätzlichen Komplexität und eigenen Kopfschmerzen verbunden.
Außerdem ist Stack vs. Heap nicht nur eine Leistungsüberlegung. Außerdem erfahren Sie viel über die erwartete Lebensdauer von Objekten.
Details finden Sie hier.
|
OK, einfach und in kurzen Worten, sie bedeuten bestellt und nicht bestellt ...!
Stapel: Bei Stapelgegenständen überlagern sich die Dinge, was bedeutet, dass die Verarbeitung schneller und effizienter ist! ...
Es gibt also immer einen Index, der auf das jeweilige Element verweist. Außerdem wird die Verarbeitung schneller, es gibt auch eine Beziehung zwischen den Elementen!
Heap: Keine Reihenfolge, die Verarbeitung wird langsamer und die Werte werden ohne bestimmte Reihenfolge oder Index durcheinander gebracht. Es gibt zufällige und es gibt keine Beziehung zwischen ihnen. Daher können Ausführung und Nutzungsdauer variieren.
Ich erstelle auch das Bild unten, um zu zeigen, wie sie aussehen können:
|
Stapel, Heap und Daten jedes Prozesses im virtuellen Speicher:
|
In den 1980er Jahren verbreitete sich UNIX wie ein Hase, und große Unternehmen rollten ihre eigenen.
Exxon hatte einen, ebenso wie Dutzende von Markennamen, die der Geschichte verloren gingen.
Wie der Speicher angelegt wurde, lag im Ermessen der vielen Implementierer.
Ein typisches C-Programm wurde flach im Speicher mit angelegt
eine Möglichkeit zur Erhöhung durch Ändern des brk () -Werts.
Typischerweise lag der HEAP knapp unter diesem Brk-Wert
und zunehmendes brk erhöhte die Menge des verfügbaren Haufens.
Der einzelne STAPEL war typischerweise ein Bereich unterhalb von HEAP, der ein Gedächtnistrakt war
enthält nichts von Wert bis zum oberen Rand des nächsten festen Speicherblocks.
Dieser nächste Block war oft CODE, der durch Stapeldaten überschrieben werden konnte
in einem der berühmten Hacks seiner Zeit.
Ein typischer Speicherblock war BSS (ein Block von Null)Werte)
die versehentlich nicht im Angebot eines Herstellers auf Null gesetzt wurde.
Ein anderes war DATA, das initialisierte Werte enthielt, einschließlich Zeichenfolgen und Zahlen.
Ein dritter war CODE, der CRT (C-Laufzeit), main, Funktionen und Bibliotheken enthielt.
Das Aufkommen des virtuellen Speichers unter UNIX ändert viele der Einschränkungen.
Es gibt keinen objektiven Grund, warum diese Blöcke zusammenhängend sein müssen.
oder in der Größe festgelegt oder jetzt auf eine bestimmte Weise bestellt.
Natürlich gab es vor UNIX Multics, die nicht unter diesen Einschränkungen litten.
Hier ist ein Schema, das eines der Speicherlayouts dieser Ära zeigt.
|
Ein paar Cent: Ich denke, es wird gut sein, Speicher grafisch und einfacher zu zeichnen:
Pfeile - zeigen an, wo der Stapel und der Heap wachsen, die Größe des Prozessstapels begrenzt ist, definiert im Betriebssystem, die Größe des Thread-Stacks durch Parameter in der Thread-Erstellungs-API, normalerweise. Heap begrenzt normalerweise die maximale Größe des virtuellen Speichers pro Prozess, z. B. für 32 Bit 2-4 GB.
So einfach: Der Prozessheap ist allgemein für den Prozess und alle darin enthaltenen Threads und wird für die Speicherzuweisung verwendet, wie dies bei malloc () üblich ist.
Der Stapel ist ein schneller Speicher zum Speichern von Funktionsrückgabezeigern und -variablen im allgemeinen Fall, die als Parameter im Funktionsaufruf und als lokale Funktionsvariablen verarbeitet werden.
|
Da einige Antworten nicht ausgewählt wurden, werde ich meine Milbe beisteuern.
Überraschenderweise hat niemand erwähnt, dass mehrere (dh nicht in Bezug auf die Anzahl der laufenden Threads auf Betriebssystemebene) Aufrufstapel nicht nur in exotischen Sprachen (PostScript) oder Plattformen (Intel Itanium), sondern auch in Fasern, grünen Threads, zu finden sind und einige Implementierungen von Coroutinen.
Fasern, grüne Fäden und Coroutinen sind in vielerlei Hinsicht ähnlich, was zu viel Verwirrung führt. Der Unterschied zwischen Fasern und grünen Fäden besteht darin, dass erstere kooperatives Multitasking verwenden, während letztere entweder kooperatives oder präventives (oder sogar beides) aufweisen können. Zur Unterscheidung zwischen Fasern und Coroutinen siehe hier.
In jedem Fall besteht der Zweck von Fasern, grünen Threads und Coroutinen darin, dass mehrere Funktionen gleichzeitig, jedoch nicht parallel (siehe diese SO-Frage zur Unterscheidung) innerhalb eines einzelnen Threads auf Betriebssystemebene ausgeführt werden und die Steuerung voneinander hin und her übertragen auf organisierte Weise.
Wenn Sie Fasern, grüne Fäden oder Coroutinen verwenden, haben Sie normalerweise einen separaten Stapel pro Funktion. (Technisch gesehen ist nicht nur ein Stapel, sondern ein ganzer Ausführungskontext pro Funktion. Am wichtigsten ist, dass sich die CPU registriert.) Für jeden Thread gibt es so viele Stapel, wie gleichzeitig Funktionen ausgeführt werden, und der Thread wechselt zwischen der Ausführung jeder Funktion gemäß der Logik Ihres Programms. Wenn eine Funktion zu Ende geht, wird ihr Stapel zerstört. Die Anzahl und Lebensdauer von Stacks ist also dynamisch und wird nicht durch die Anzahl der Threads auf Betriebssystemebene bestimmt!
Beachten Sie, dass ich sagte "normalerweise einen separaten Stapel pro Funktion haben". Es gibt sowohl stapelbare als auch stapellose Implementierungen von Couroutinen. Die bemerkenswertesten stapelbaren C ++ - Implementierungen sind Boost.Coroutine und async / await von Microsoft PPL. (Die wiederaufnehmbaren Funktionen von C ++ (a.k.a. "async and await"), die für C ++ 17 vorgeschlagen wurden, verwenden wahrscheinlich stapellose Coroutinen.)
Der Vorschlag für Fasern für die C ++ - Standardbibliothek ist in Vorbereitung. Es gibt auch einige Bibliotheken von Drittanbietern. Grüne Fäden sind in Sprachen wie Python und Ruby äußerst beliebt.
|
Ich habe etwas zu teilen, obwohl die wichtigsten Punkte bereits behandelt werden.
Stapel
Sehr schneller Zugang.
Im RAM gespeichert.
Hier werden Funktionsaufrufe zusammen mit den übergebenen lokalen Variablen und Funktionsparametern geladen.
Der Speicherplatz wird automatisch freigegeben, wenn das Programm einen Bereich verlässt.
Im sequentiellen Speicher gespeichert.
Haufen
Langsamer Zugriff im Vergleich zu Stack.
Im RAM gespeichert.
Hier werden dynamisch erstellte Variablen gespeichert, die später die Freigabe des zugewiesenen Speichers nach der Verwendung erfordern.
Wird überall dort gespeichert, wo die Speicherzuweisung erfolgt, und wird immer mit dem Zeiger aufgerufen.
Interessanter Hinweis:
Wenn die Funktionsaufrufe auf einem Heap gespeichert worden wären, hätte dies zu 2 unordentlichen Punkten geführt:
Aufgrund der sequentiellen Speicherung im Stapel ist die Ausführung schneller. Die Speicherung auf dem Heap hätte zu einem enormen Zeitaufwand geführt, wodurch das gesamte Programm langsamer ausgeführt würde.
Wenn Funktionen im Heap gespeichert wären (unordentlicher Speicher, auf den der Zeiger zeigt), hätte es keine Möglichkeit gegeben, zur Aufruferadresse zurückzukehren (die der Stapel aufgrund der sequentiellen Speicherung im Speicher angibt).
|
Wow! So viele Antworten und ich glaube nicht, dass einer von ihnen es richtig gemacht hat ...
1) Wo und was sind sie (physisch im Speicher eines realen Computers)?
Der Stapel ist ein Speicher, der als höchste Speicheradresse beginnt, die Ihrem Programmabbild zugewiesen wurde, und von dort aus an Wert verliert. Es ist für aufgerufene Funktionsparameter und für alle in Funktionen verwendeten temporären Variablen reserviert.
Es gibt zwei Haufen: öffentliche und private.
Der private Heap beginnt an einer 16-Byte-Grenze (für 64-Bit-Programme) oder einer 8-Byte-Grenze (für 32-Bit-Programme) nach dem letzten Codebyte in Ihrem Programm und nimmt dann zuWert von dort. Es wird auch als Standardheap bezeichnet.
Wenn der private Heap zu groß wird, überlappt er den Stapelbereich, ebenso wie der Stapel den Heap, wenn er zu groß wird. Da der Stapel an einer höheren Adresse beginnt und sich zu einer niedrigeren Adresse hinunterarbeitet, können Sie den Stapel bei ordnungsgemäßem Hacking so groß machen, dass er den privaten Heap-Bereich überläuft und den Codebereich überlappt. Der Trick besteht dann darin, den Codebereich so weit zu überlappen, dass Sie ihn in den Code einbinden können. Es ist etwas schwierig und Sie riskieren einen Programmabsturz, aber es ist einfach und sehr effektiv.
Der öffentliche Heap befindet sich in seinem eigenen Speicherbereich außerhalb Ihres Programmabbildbereichs. Dieser Speicher wird auf die Festplatte übertragen, wenn die Speicherressourcen knapp werden.
2) Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Der Stack wird vom Programmierer gesteuert, der private Heap wird vom Betriebssystem verwaltet und der öffentliche Heap wird von niemandem gesteuert, da es sich um einen Betriebssystemdienst handelt. Sie stellen Anforderungen und entweder werden sie gewährt oder abgelehnt.
2b) Was ist ihr Umfang?
Sie sind alle global für das Programm, aber ihre Inhalte können privat, öffentlich oder global sein.
2c) Was bestimmt die Größe von jedem von ihnen?
Die Größe des Stapels und des privaten Heaps wird durch die Laufzeitoptionen Ihres Compilers bestimmt. Der öffentliche Heap wird zur Laufzeit mithilfe eines Größenparameters initialisiert.
2d) Was macht einen schneller?
Sie sind nicht so konzipiert, dass sie schnell sind, sondern so, dass sie nützlich sind. Wie der Programmierer sie verwendet, bestimmt, ob sie "schnell" oder "langsam" sind.
REF:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
|
Viele Antworten sind als Konzepte korrekt, aber wir müssen beachten, dass die Hardware (d. H. Der Mikroprozessor) einen Stapel benötigt, um das Aufrufen von Unterroutinen zu ermöglichen (CALL in Assemblersprache ..). (OOP Jungs werden es Methoden nennen)
Auf dem Stack speichern Sie die Absenderadressen und Aufruf → Push / Ret → Pop wird direkt in der Hardware verwaltet.
Sie können den Stapel verwenden, um Parameter zu übergeben. Auch wenn er langsamer ist als die Verwendung von Registern (würde ein Mikroprozessor-Guru sagen oder ein gutes BIOS-Buch aus den 1980er Jahren ...)
Ohne Stack kann kein Mikroprozessor arbeiten. (Wir können uns kein Programm vorstellen, auch nicht in Assemblersprache, ohne Unterprogramme / Funktionen)
Ohne den Haufen kann es. (Ein Assembler-Programm kann ohne funktionieren, da der Heap ein Betriebssystemkonzept ist, als Malloc, dh ein OS / Lib-Aufruf.
Die Stapelnutzung ist schneller als:
Ist Hardware und sogar Push / Pop sind sehr effizient.
Für malloc muss der Kernelmodus aufgerufen, Lock / Semaphore (oder andere Synchronisationsprimitive) verwendet werden, um Code auszuführen, und einige Strukturen verwaltet werden, die zur Verfolgung der Zuordnung erforderlich sind.
|
Heap ist ein Bereich mit dynamisch zugewiesenem Speicher, der automatisch vom Betriebssystem oder der Speichermanager-Bibliothek verwaltet wird. Sie können einen Block jederzeit zuweisen und jederzeit freigeben. Für die Heap-Zuweisung muss ein vollständiger Datensatz darüber geführt werden, welcher Speicher zugewiesen ist und welcher nicht. Außerdem müssen einige Overhead-Wartungsarbeiten durchgeführt werden, um die Fragmentierung zu verringern. Der Speicher kann jederzeit freigegeben werden, sodass freier Speicherplatz verbleibt. Wenn der Heap wächst, werden häufig neue Blöcke von niedrigeren Adressen zu höheren Adressen zugewiesen. Sie können sich den Heap also als einen Heap von Speicherblöcken vorstellen, dessen Größe mit der Zuweisung von Speicher zunimmt. Wenn der Heap für eine Zuordnung zu klein ist, kann die Größe häufig erhöht werden, indem mehr Speicher vom zugrunde liegenden Betriebssystem abgerufen wird. Der vom Heap zugewiesene Speicher bleibt so lange zugewiesen, bis einer der folgenden Ereignisse eintritt:
Der Speicher wird freigegeben
Das Programm wird beendet
Stapel:
Wie der Heap im Computer-RAM gespeichert.
Auf dem Stapel erstellte Variablen verlassen den Gültigkeitsbereich und werden automatisch freigegeben.
Im Vergleich zu Variablen auf dem Heap viel schneller zuzuweisen.
Speichert lokale Daten, Rücksprungadressen, die für die Parameterübergabe verwendet werden.
Kann einen Stapelüberlauf haben, wenn zu viel des Stapels verwendet wird (meistens)
von unendlicher oder zu tiefer Rekursion, sehr großen Zuordnungen).
Sie würden den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie benötigen
vor der Kompilierungszeit zuweisen und es ist nicht zu groß.
Normalerweise hat eine maximale Größe bereits bei Ihrem Programm festgelegt
beginnt.
Haufen:
Wird wie der Stack im Computer-RAM gespeichert.
In C ++ müssen Variablen auf dem Heap manuell und niemals zerstört werden
aus dem Rahmen fallen.
Die Daten werden mit delete, delete [] oder free freigegeben.
Langsameres Zuweisen im Vergleich zu Variablen auf dem Stapel.
Wird bei Bedarf verwendet, um einen Datenblock zur Verwendung durch das Programm zuzuweisen.
Kann fragmentiert sein, wenn es viele Zuordnungen gibt und
Freigabe.
In C ++ oder C werden auf dem Heap erstellte Daten durch Zeiger angezeigt
und mit neu bzw. malloc zugeordnet.
Kann Zuordnungsfehler aufweisen, wenn ein zu großer Puffer angefordert wird
zugeteilt werden.
Duwürde den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie haben
wird zur Laufzeit benötigt oder wenn Sie viele Daten zuordnen müssen.
Verantwortlich für Speicherlecks.
|
Der Stapel ist im Wesentlichen ein leicht zugänglicher Speicher, der seine Elemente einfach verwaltet
als - gut - Stapel. Nur Gegenstände, deren Größe im Voraus bekannt ist, können auf den Stapel gelegt werden. Dies ist bei Zahlen, Zeichenfolgen und Booleschen Werten der Fall.
Der Heap ist ein Speicher für Elemente, für die Sie die nicht vorbestimmen können
genaue Größe und Struktur. Da Objekte und Arrays mutiert werden können und
Zur Laufzeit ändern, müssen sie in den Haufen gehen.
Quelle: Academind
|
CPU-Stack und -Heap hängen physisch damit zusammen, wie CPU und Register mit dem Speicher arbeiten, wie die Maschinenassemblierungssprache funktioniert, nicht mit Hochsprachen selbst, selbst wenn diese Sprachen kleine Dinge entscheiden können.
Alle modernen CPUs arbeiten mit der "gleichen" Mikroprozessortheorie: Sie basieren alle auf sogenannten "Registern" und einige dienen dazu, dass "Stack" an Leistung gewinnt. Alle CPUs haben von Anfang an Stapelregister und waren, wie ich weiß, immer hier, sozusagen. Assemblersprachen sind von Anfang an dieselben, trotz Variationen ... bis hin zu Microsoft und seiner Intermediate Language (IL), die das Paradigma geändert haben, eine Assemblersprache für virtuelle OO-Maschinen zu haben. So können wir in Zukunft eine CLI / CIL-CPU haben (ein Projekt von MS).
CPUs verfügen über Stapelregister, um den Speicherzugriff zu beschleunigen. Sie sind jedoch im Vergleich zur Verwendung anderer Register begrenzt, um vollen Zugriff auf den gesamten verfügbaren Speicher für den Prozess zu erhalten. Deshalb haben wir über Stapel- und Heap-Zuordnungen gesprochen.
Zusammenfassend und im Allgemeinen ist der Heap hudge und langsam und gilt für "globale" Instanzen und Objektinhalte, da der Stapel klein und schnell ist und für "lokale" Variablen und Referenzen (versteckte Zeiger, die vergessen werden, sie zu verwalten).
Wenn wir also das neue Schlüsselwort in einer Methode verwenden, wird die Referenz (ein int) im Stapel erstellt, aber das Objekt und sein gesamter Inhalt (Werttypen sowie Objekte) werden im Heap erstellt, wenn ich mich erinnere. Im Stapel werden jedoch lokale Elementarwerttypen und Arrays erstellt.
Der Unterschied beim Speicherzugriff liegt auf der Ebene, auf die sich die Zellen beziehen: Das Adressieren des Heaps, des Gesamtspeichers des Prozesses, erfordert eine komplexere Handhabung der CPU-Register als der Stapel, der lokal "adressierter" ist, weil der CPU-Stapel Wenn ich mich recht erinnere, wird das Register als Basisadresse verwendet.
Aus diesem Grund kam es bei sehr langen oder unendlichen wiederkehrenden Aufrufen oder Schleifen schnell zu einem Stapelüberlauf, ohne das System auf modernen Computern einzufrieren ...
C # Heap (ing) Vs Stack (ing) In .NET
Stapel gegen Haufen: Kennen Sie den Unterschied
Statische Klassenspeicherzuordnung, in der C # gespeichert ist
Was und wo sind der Stapel und der Haufen?
https://en.wikipedia.org/wiki/Memory_management
https://en.wikipedia.org/wiki/Stack_register
Assembler-Ressourcen:
Tutorial zur Baugruppenprogrammierung
Softwareentwicklerhandbücher für Intel® 64- und IA-32-Architekturen
|
Vielen Dank für eine wirklich gute Diskussion, aber als echter Neuling frage ich mich, wo Anweisungen aufbewahrt werden? Am Anfang entschieden sich Wissenschaftler zwischen zwei Architekturen (von NEUMANN, wo alles als DATA betrachtet wird, und HARVARD, wo ein Speicherbereich für Anweisungen und ein anderer für Daten reserviert war). Letztendlich haben wir uns für das von Neumann-Design entschieden und jetzt gilt alles als "gleich". Das machte es mir schwer, als ich Montage lernte
https://www.cs.virginia.edu/~evans/cs216/guides/x86.html
weil sie über Register und Stapelzeiger sprechen.
Alles oben spricht über DATEN. Ich vermute, dass ein Befehl, da er eine definierte Sache mit einem bestimmten Speicherbedarf ist, auf dem Stapel abgelegt wird und sich daher alle in dieser Assembly diskutierten Register auf dem Stapel befinden. Natürlich kam dann die objektorientierte Programmierung mit Anweisungen und Daten, die in eine dynamische Struktur kamen, sodass jetzt auch Anweisungen auf dem Heap bleiben würden?
|
Sehr aktive Frage. Verdiene 10 Reputationen, um diese Frage zu beantworten. Die Reputationsanforderung schützt diese Frage vor Spam und nicht beantworteten Aktivitäten.
Nicht die Antwort, die Sie suchen? Durchsuchen Sie andere Fragen mit dem Tag "Sprachverwaltungsstapel", sprachunabhängig, Heap, dynamische Speicherzuweisung, oder stellen Sie Ihre eigene Frage.